home *** CD-ROM | disk | FTP | other *** search
- /*
- * Board.c - "Boxes" playing board manager
- */
-
- # include "Boxes.h"
-
-
- # define sideLen 30 /* should be even for best results */
- # define dotSize 9 /* should be odd for best results */
- # define linkWidth 3 /* should be odd for best results */
-
-
- /*
- * Initial number of squares and dots horizontally/vertically.
- */
-
- short hSquares = 5;
- short vSquares = 5;
-
- # define hDots (hSquares + 1)
- # define vDots (vSquares + 1)
-
-
- /* square side bitmasks */
-
- # define leftMask 0x0001
- # define topMask 0x0002
- # define rightMask 0x0004
- # define bottomMask 0x0008
- # define allMask 0x000f /* all sides */
-
-
- short sidesLeft;
-
- /*
- * Square and Side data structure stuff.
- */
-
- typedef struct Square Square;
-
- struct Square
- {
- short owner; /* index of player owning square. -1 = not owned yet */
- short sides; /* bit map indicating sides that are on. 0 = none, 15 = all */
- };
-
- static Square square[maxSquares][maxSquares];
-
-
- /*
- * Sides are initialized to -1. When a player hits them, they are set to
- * the player number (0..maxPlayer-1). When a side is drawn, it's drawn in
- * the player's color.
- */
-
- static short hSide[maxSquares][maxSquares + 1];
- static short vSide[maxSquares + 1][maxSquares];
-
- static RgnHandle ownerRgn = nil;
-
-
- /* Local routine prototypes */
-
- static RgnHandle ProtoOwnerRgn (void);
- static RgnHandle OwnerRgn (short, short);
-
-
- void
- GetBoardSize (short *h, short *v)
- {
- *h = hSquares;
- *v = vSquares;
- }
-
-
- void
- SetBoardSize (short h, short v)
- {
- hSquares = h;
- vSquares = v;
- }
-
-
- void
- InitializeBoard (void)
- {
- short h, v;
- Square *s;
- Rect r;
- RgnHandle rgn;
-
- for (h = 0; h < hDots; h++)
- {
- for (v = 0; v < vDots; v++)
- {
- s = &square[h][v];
- s->owner = -1;
- s->sides = 0;
- }
- }
- for (h = 0; h < hSquares; h++)
- {
- for (v = 0; v < vDots; v++)
- hSide[h][v] = -1;
- }
- for (h = 0; h < hDots; h++)
- {
- for (v = 0; v < vSquares; v++)
- vSide[h][v] = -1;
- }
- sidesLeft = (hSquares + 1) * vSquares + hSquares * (vSquares + 1);
-
- /*
- * Calculate prototypical region inside a completed box. This is the
- * square, minus the overlap of the squares with the links, minus the
- * overlap of the square with the dots at the corners.
- */
-
- if (ownerRgn == nil)
- ownerRgn = ProtoOwnerRgn ();
- }
-
-
- static RgnHandle
- ProtoOwnerRgn (void)
- {
- Rect r;
- RgnHandle rgn, rgn2;
-
- SetRect (&r, 1, 1, sideLen, sideLen);
- InsetRect (&r, (linkWidth - 1) / 2, (linkWidth - 1) / 2);
- if ((rgn = NewRgn ()) == nil)
- return (nil);
- if ((rgn2 = NewRgn ()) == nil)
- {
- DisposeRgn (rgn);
- return (nil);
- }
- RectRgn (rgn, &r);
- SetRect (&r, 0, 0, dotSize, dotSize);
- OffsetRect (&r, -(dotSize - 1) / 2, -(dotSize - 1) / 2);
- RectRgn (rgn2, &r);
- DiffRgn (rgn, rgn2, rgn); /* subtract upper left corner */
- OffsetRgn (rgn2, sideLen, 0);
- DiffRgn (rgn, rgn2, rgn); /* subtract upper right corner */
- OffsetRgn (rgn2, 0, sideLen);
- DiffRgn (rgn, rgn2, rgn); /* subtract lower right corner */
- OffsetRgn (rgn2, -sideLen, 0);
- DiffRgn (rgn, rgn2, rgn); /* subtract lower left corner */
- DisposeRgn (rgn2);
- return (rgn); /* this must be disposed of by caller */
- }
-
-
- /*
- * Calculate the region inside a completed box. This is a copy of the
- * prototypical owner region, offset by the appropriate amount.
- */
-
- static RgnHandle
- OwnerRgn (short h, short v)
- {
- RgnHandle rgn, rgn2;
-
- if ((rgn = NewRgn ()) == nil)
- return (nil);
- CopyRgn (ownerRgn, rgn);
- OffsetRgn (rgn, hPad + h * sideLen, vPad + v * sideLen);
- return (rgn); /* this must be disposed of by caller */
- }
-
-
- /*
- * Calculate size of rectangle in which board is drawn. This is actually
- * inaccurate; it's the recangle that goes from the center of the upper left
- * dot to the center of the lower right dot.
- */
-
- void
- CalcBoardSize (Rect *r)
- {
- SetRect (r, 0, 0, hSquares * sideLen, vSquares * sideLen);
- }
-
-
- short
- SidesLeft (void)
- {
- return (sidesLeft);
- }
-
-
- /*
- * Add a side to a square.
- */
-
- static void
- AddSide (short h, short v, short mask)
- {
- if (h < 0 || h >= hSquares || v < 0 || v >= vSquares)
- return; /* coordinates out of range */
- square[h][v].sides |= mask;
- if (square[h][v].sides == allMask)
- {
- square[h][v].owner = GetPlayer ();
- DrawOwner (h, v);
- }
- }
-
-
- static Boolean
- WillCompleteBox (short h, short v, short sideMask)
- {
- if (h < 0 || h >= hSquares || v < 0 || v >= vSquares)
- return (false); /* coordinates out of range */
- if (square[h][v].sides == allMask)
- return (false); /* already complete */
- return ((square[h][v].sides | sideMask) == allMask ? true : false);
- }
-
-
- short
- SetHSide (short h, short v, short player)
- {
- short squares;
-
- squares = WillCompleteBox (h, v, topMask);
- squares += WillCompleteBox (h, v - 1, bottomMask);
- hSide[h][v] = player;
- --sidesLeft;
- DrawHSide (h, v);
- AddSide (h, v, topMask);
- AddSide (h, v - 1, bottomMask);
- return (squares);
- }
-
-
- short
- SetVSide (short h, short v, short player)
- {
- short squares;
-
- squares = WillCompleteBox (h, v, leftMask);
- squares += WillCompleteBox (h - 1, v, rightMask);
- vSide[h][v] = player;
- --sidesLeft;
- DrawVSide (h, v);
- AddSide (h, v, leftMask);
- AddSide (h - 1, v, rightMask);
- return (squares);
- }
-
-
- /*
- * Board hit testing routines. These are not the most efficient, algorithmically,
- * but they make fairly clear what is being tested through the use of a rect that
- * is moved around to the positions between pairs of adjacent dots. The hittable
- * areas between dots are considered to be as wide as the dot although the links
- * themselves are only drawn 3 pixels wide. This is to make them easier to hit.
- */
-
-
- Boolean
- HLinkHitTest (Point pt, short *hval, short *vval)
- {
- short h, v;
- Rect r, r2;
-
- /* calculate rect between two leftmost dots in upper row */
- SetRect (&r, 0, 0, sideLen - dotSize, dotSize);
- OffsetRect (&r, hPad + (dotSize + 1) / 2, vPad - (dotSize - 1) / 2);
-
- for (v = 0; v < vDots; v++) /* for each row */
- {
- r2 = r;
- for (h = 0; h < hSquares; h++) /* for each column in row */
- {
- if (PtInRect (pt, &r2))
- {
- if (hSide[h][v] != noPlayer) /* it's already been clicked */
- return (false);
- *hval = h;
- *vval = v;
- return (true);
- }
- OffsetRect (&r2, sideLen, 0); /* move to next column */
- }
- OffsetRect (&r, 0, sideLen); /* move to next row */
- }
- return (false);
- }
-
-
- Boolean
- VLinkHitTest (Point pt, short *hval, short *vval)
- {
- short h, v;
- Rect r, r2;
-
- /* calculate rect between two uppermost dots in left row */
- SetRect (&r, 0, 0, dotSize, sideLen - dotSize);
- OffsetRect (&r, hPad - (dotSize - 1) / 2, vPad + (dotSize + 1) / 2);
-
- for (h = 0; h < hDots; h++) /* for each row */
- {
- r2 = r;
- for (v = 0; v < vSquares; v++) /* for each column */
- {
- if (PtInRect (pt, &r2))
- {
- if (vSide[h][v] != noPlayer) /* it's already been clicked */
- return (false);
- *hval = h;
- *vval = v;
- return (true);
- }
- OffsetRect (&r2, 0, sideLen); /* move to next row */
- }
- OffsetRect (&r, sideLen, 0); /* move to next column */
- }
- return (false);
- }
-
-
- /*
- * Board drawing routines. DrawHSide() and DrawVSide() are called only for
- * sides that have been clicked.
- */
-
- void
- DrawHSide (short h, short v)
- {
- short len;
-
- SetColor (GetPlayerColor (hSide[h][v]));
- PenSize (1, linkWidth);
- h = hPad + h * sideLen + (dotSize + 1) / 2;
- v = vPad + v * sideLen - (linkWidth - 1) / 2;
- MoveTo (h, v);
- LineTo (h + sideLen - dotSize - 1, v);
- PenNormal ();
- RestoreColor ();
- }
-
-
- void
- DrawVSide (short h, short v)
- {
- short len;
-
- SetColor (GetPlayerColor (vSide[h][v]));
- PenSize (linkWidth, 1);
- h = hPad + h * sideLen - (linkWidth - 1) / 2;
- v = vPad + v * sideLen + (dotSize + 1) / 2;
- MoveTo (h, v);
- LineTo (h, v + sideLen - dotSize - 1);
- PenNormal ();
- RestoreColor ();
- }
-
-
- void
- DrawGrid (short cols, short rows,
- short hOffset, short vOffset,
- short hSize, short vSize)
- {
- short i, h, v, len;
-
- /* draw vertical lines */
- len = (rows - 1) * vSize;
- for (i = 0; i < cols; i++)
- {
- h = hOffset + i * hSize;
- MoveTo (h, vOffset);
- LineTo (h, vOffset + len);
- }
- /* draw horizontal lines */
- len = (cols - 1) * hSize;
- for (i = 0; i < rows; i++)
- {
- v = vOffset + i * vSize;
- MoveTo (hOffset, v);
- LineTo (hOffset + len, v);
- }
- }
-
-
- void
- DrawOwner (short h, short v)
- {
- Str255 s;
- short width;
- RgnHandle rgn;
- short owner = square[h][v].owner;
-
- if (owner != -1)
- {
- SetColor (GetPlayerColor (owner));
- if ((rgn = OwnerRgn (h, v)) != nil)
- {
- InsetRgn (rgn, 1, 1);
- FrameRgn (rgn);
- DisposeRgn (rgn);
- }
- NumToString ((long) owner + 1, s);
- width = StringWidth (s);
- MoveTo (hPad + h * sideLen + (sideLen- width) / 2,
- vPad + (v+1) * sideLen - (sideLen - fontHeight) / 2 - fontInfo.descent);
- DrawString (s);
- RestoreColor ();
- }
- }
-
-
- void
- DrawBoard (void)
- {
- short h, v;
- Rect r;
-
- SetColor (GetSideColor ());
- PenPat (gray);
- DrawGrid (hDots, vDots, hPad, vPad, sideLen, sideLen);
- PenNormal ();
- SetColor (GetDotColor ());
- for (h = 0; h < hDots; h++)
- {
- for (v = 0; v < vDots; v++)
- {
- SetRect (&r, 0, 0, dotSize, dotSize);
- OffsetRect (&r, hPad - (dotSize - 1) / 2 + h * sideLen,
- vPad - (dotSize - 1) / 2 + v * sideLen);
- PaintRect (&r);
- }
- }
- RestoreColor ();
- for (h = 0; h < hSquares; h++)
- {
- for (v = 0; v < vDots; v++)
- {
- if (hSide[h][v] != -1)
- DrawHSide (h, v);
- }
- }
- for (h = 0; h < hDots; h++)
- {
- for (v = 0; v < vSquares; v++)
- {
- if (vSide[h][v] != -1)
- DrawVSide (h, v);
- }
- }
- for (h = 0; h < hSquares; h++)
- {
- for (v = 0; v < vSquares; v++)
- DrawOwner (h, v);
- }
- }
-